iT邦幫忙

2021 iThome 鐵人賽

DAY 2
0
Software Development

猴子都寫得出來的 RISC-V CPU Emulator系列 第 14

RISC-V: U-type Load Immediate指令

  • 分享至 

  • xImage
  •  

每日一 Bug,
其實作天就有發現 get_imm() 運算結果怪怪的,
如果會 Sign-Extension,怎麼 get_imm(24, 20) 結果竟然是 31,
如果不會 Sign-Estension,前面的 get_imm(31, 20) 怎麼會回傳負數?
大家可以先猜猜問題出在哪,下面再為大家解答!
先給一點提示:讀文件,治百病

這次修改的範圍剛好會碰到在 Register File 生灰塵的 Program Counter,
就順便讓它動起來吧!

另外特別感謝 Ruinland Maskman 的發文:RISC-V core上porting Linux
在研究這個系統的過程中,發現了另外一個 RISC-V Emulator exactstep
各方面實作很完整,指令解碼方式跟 RISC-V-TLM 也完全不一樣,
對設計新的 Instruction Decoder 很有幫助。

U-type

指令格式如下:

|31                      12|11   7|6       0|
+-------------------------------------------+
|            imm           |  rd  | opcode  |
+-------------------------------------------+

LUI

採用 20 bit Asymmetric 的設計,
搭配 I-type 指令就可以指定任何想要的數值。
rd = imm << 12;

|31                      12|11   7|6       0|
+-------------------------------------------+
|            imm           |  rd  | opcode  |
+-------------------------------------------+

AUIPC

imm 設定為 0 可以取得 Program Counter,
規格書上有提到其實 JALR 也一樣可以有一樣的效果,
但是會影響 Pipeline 和 Branch Predictor 系統的運作流程。

目前沒有使用這道指令的經驗,
看起來跟 Boot Loader 準備好 MMU 設定之後跳過去指定位置執行有關,
有看過的夥伴歡迎幫忙補充。
rd = current_pc + (imm << 12);

|31                      12|11   7|6       0|
+-------------------------------------------+
|            imm           |  rd  | opcode  |
+-------------------------------------------+

實際程式

github 頁面 Tag: ITDay14
寫 Tag 寫這麼多天,剛剛才發現根本忘記推上 github...

首先先來解決那個奇怪的 Bug:
根據 SystemC 2.3.1 文件所述,
sc_int<32> 只有在 Assign 之後才會執行 extend_sign()

//sc_int_base.h
sc_int_base& operator <<= ( int_type v )
    { m_val <<= v; extend_sign(); return *this; }

照上面的描述可以知道,
單純的 << Operator 是不會進行 sign extension 的,
需要 Assign 才行,
改成下面這樣就沒問題了:

//instructionDecoder.cpp
int32_t INSTRUCTION_DECODER::get_imm(uint32_t end, uint32_t start)
{
    auto value = sc_dt::sc_int<32>(instruction_value);
    value <<= (31-end);
    value >>= (31 - end + start);
	return   value;
}

這次剛好用到 Program Counter 相關的功能,
就順便做完它。
看起來是個小小的功能,做起來其實很有趣,
參考了相對成熟的 RISC-V-TLMexactstep
前者是在執行後回傳 PC 是否需要 +4,後者是直接在每道指令執行時決定,
一個會在每個 Ececute Function 開不必要的介面,
另一個會多寫很多不必要的程式碼增加修改時錯誤的機率。

最後決定自己換一個方法:
EXECUTOR 中增加一個暫存的 new_pc ,
在執行指令前設定為 current_pc + 4,
在由執行中的指令依照需求覆蓋,並在最後寫入。

...
//executor.h
uint32_t new_pc; //write back to register when execute finished
...
//executor.cpp
void EXECUTOR::execute()
{
	new_pc = register_file->get_pc() + 4;
...
	register_file->set_pc(new_pc);
}

指令的部分相對單純

//instructionDecoderInterface.h
...
		LUI_OP = 0b0110111,
		AUIPC_OP = 0b0010111,
...
//executor.cpp
...

		case INSTRUCTION_DECODER_INTERFACE::LUI_OP:
			LUI_E();
			break;
		case INSTRUCTION_DECODER_INTERFACE::AUIPC_OP:
			AUIPC_E();
			break;
...
void EXECUTOR::LUI_E()
{
	auto rd = instruction_decoder->get_rd();

	auto value = (instruction_decoder->get_imm(31, 12) << 12);
	register_file->set_value_integer(rd, value);
...
}
void EXECUTOR::AUIPC_E()
{
	auto rd = instruction_decoder->get_rd();

	auto value = register_file->get_pc() + (instruction_decoder->get_imm(31, 12) << 12);
	register_file->set_value_integer(rd, value);
...
}
...

上一篇
RISC-V: I-type 移位指令
下一篇
RISC-V: Memory Load指令
系列文
猴子都寫得出來的 RISC-V CPU Emulator31
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言